package cn.ad.server.persistence;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import cn.ad.server.common.utils.Constants;
import cn.ad.server.persistence.dao.AdMaterialDao;
import cn.ad.server.persistence.dao.AdMaterialGroupDao;
import cn.ad.server.persistence.dao.AdSizeDao;
import cn.ad.server.persistence.dao.AdTemplateDao;
import cn.ad.server.persistence.dao.AdTemplateSpecDao;
import cn.ad.server.persistence.dao.CreativeDao;
import cn.ad.server.domain.ad.AdMaterial;
import cn.ad.server.domain.ad.AdMaterialGroup;
import cn.ad.server.domain.ad.AdSize;
import cn.ad.server.domain.ad.AdTemplate;
import cn.ad.server.domain.ad.AdTemplateSpec;
import cn.ad.server.domain.ad.Creative;

@Repository
public class CreativeRepositoryImpl implements CreativeRepository {

	static final Logger logger = LoggerFactory.getLogger(CreativeRepositoryImpl.class);

	@Autowired
	private CreativeDao creativeDao;

	@Autowired
	private AdMaterialDao adMaterialDao;

	@Autowired
	private AdSizeDao adSizeDao;

	@Autowired
	private AdTemplateDao adTemplateDao;

	@Autowired
	private AdMaterialGroupDao adMaterialGroupDao;

	@Autowired
	private AdTemplateSpecDao adTemplateSpecDao;

	@Resource(name = "cacheRedisTemplate")
	private RedisTemplate<String, String> redisTemplate;

	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, Creative> creativeValueOperations;

	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, AdMaterial> adMaterialsValueOperations;

	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, AdMaterialGroup> adMaterialGroupValueOperations;

	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, List<Integer>> mappingValueOperations;

	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, AdTemplate> adTemplateOperations;

	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, AdTemplateSpec> adTemplateSpecOperations;

	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, String> stringValueOperations;

	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, Long> countOperations;

	@Override
	public void warmUpCreative() {

		String warmUpTime = stringValueOperations.get(Constants.WARM_UP_TIME);
		List<Creative> creatives = null;
		if (StringUtils.isBlank(warmUpTime))
			creatives = creativeDao.findAll();
		else
			creatives = creativeDao.findAfterTime(warmUpTime);
		if (CollectionUtils.isEmpty(creatives))
			return;
		for (Creative c : creatives) {
			if (c.getStatus() == 1) {
				List<AdMaterial> adMaterials = adMaterialDao.findByCreativeId(c.getId());
				c.setAdMaterials(adMaterials);

				creativeValueOperations.set(c.toCacheKey(), c);
				// warmUpCreativeAndAdMaterialMap(c.getId());
				warmUpCreativeAndMaterialGroupMap(c.getId());
			} else if (c.getStatus() == -1) {
				redisTemplate.delete(c.toCacheKey());
				redisTemplate.delete(Creative.materialsIdsCacheKey(c.getId()));
			}
		}

	}

	@Override
	public void warmUpAdMaterials() {

		String warmUpTime = stringValueOperations.get(Constants.WARM_UP_TIME);
		List<AdMaterial> ams = adMaterialDao.findAll();
		if (CollectionUtils.isEmpty(ams))
			return;
		for (AdMaterial am : ams) {
			try {
				if (am.getStatus() == 1) {
					am = attachAdSize(am);
					if (am != null) {
						adMaterialsValueOperations.set(am.toCacheKey(), am);
					}
				} else if (am.getStatus() != 1) {
					redisTemplate.delete(am.toCacheKey());
				}
			} catch (Exception e) {
				if (am != null) {
					logger.error("错误：在获取AdMaterial_id =" + am.getId() + "数据时发生错误 \n 错误信息：", e);
				}
			}
		}
	}

	public final static int STATUS_VALID = 1;

	/**
	 *
	 * @param adMaterial
	 * @return
	 */
	private AdMaterial attachAdSize(AdMaterial adMaterial) {
		if (adMaterial == null) {
			return null;
		}
		// adSize
		AdSize adSize = adMaterial.getAdSize();
		if (adSize != null) {
			adSize = adSizeDao.selectById(adMaterial.getAdSize().getId(), STATUS_VALID);
			adMaterial.setAdSize(adSize);
		}
		return adMaterial;
	}

	public void warmUpCreativeAndAdMaterialMap(long creativeId) {

		List<Integer> mIds = adMaterialDao.findIds(creativeId);
		if (CollectionUtils.isEmpty(mIds))
			return;
		mappingValueOperations.set(Creative.materialsIdsCacheKey(creativeId), mIds);
	}

	@Override
	public Creative findById(long id) {
		return creativeValueOperations.get(Creative.cacheKey(id));

	}

	@Override
	public List<AdMaterial> findAdMaterials(long id) {
		long s = System.currentTimeMillis();
		List<Integer> mIds = mappingValueOperations.get(Creative.materialsIdsCacheKey(id));
		logger.debug("mids--------- {}", System.currentTimeMillis() - s);
		if (CollectionUtils.isEmpty(mIds))
			return null;
		List<AdMaterial> ams = new ArrayList<AdMaterial>();
		for (long amId : mIds) {
			ams.add(findAdMaterialById(amId));
		}
		logger.debug("ams--------- {}", System.currentTimeMillis() - s);
		return ams;
	}

	@Override
	public AdMaterial findAdMaterialById(long id) {
		return adMaterialsValueOperations.get(AdMaterial.cacheKey(id));
	}

	@Override
	public List<AdMaterialGroup> findAdMaterialGroup(Creative c) {
		List<AdMaterialGroup> amgs = new ArrayList<AdMaterialGroup>();
		List<Integer> amgIds = findCreativeAdMaterialGroupIds(c.getId());
		for (Integer amgId : amgIds) {
			AdMaterialGroup amg = findAdMaterialGroupById(amgId);
			amg.setCreative(c);
			amgs.add(amg);
		}
		return amgs;
	}

	AdMaterialGroup findAdMaterialGroupById(int id) {
		AdMaterialGroup amg = adMaterialGroupValueOperations.get(AdMaterialGroup.cacheKey(id));
		amg.setAdMaterial(this.findAdMaterialById(amg.getAdMaterial().getId()));
		amg.setAdTemplate(this.findAdTemplateById(amg.getAdTemplate().getId()));
		amg.setAdTemplateSpec(this.findAdTemplateSpecById(amg.getAdTemplateSpec().getId()));
		return amg;
	}

	public AdTemplate findAdTemplateById(int id) {
		return adTemplateOperations.get(AdTemplate.cacheKey(id));
	}

	List<Integer> findCreativeAdMaterialGroupIds(long creativeId) {
		return mappingValueOperations.get(Creative.materialGroupIdsCacheKey(creativeId));
	}

	@Override
	public void warmUpAdMaterialGroup() {
		String warmUpTime = stringValueOperations.get(Constants.WARM_UP_TIME);
		List<AdMaterialGroup> amgs = null;
		if (StringUtils.isEmpty(warmUpTime)) {
			amgs = adMaterialGroupDao.findAll();
		} else {
			amgs = adMaterialGroupDao.findAfterTime(warmUpTime);
		}
		if (CollectionUtils.isEmpty(amgs))
			return;
		for (AdMaterialGroup amg : amgs) {
			amg.setAdMaterial(adMaterialDao.findById(amg.getAdMaterial().getId()));
			amg.setAdTemplateSpec(adTemplateSpecDao.findById(amg.getAdTemplateSpec().getId()));
			amg.setAdTemplate(adTemplateDao.findById(amg.getAdTemplate().getId()));
			this.adMaterialGroupValueOperations.set(amg.toCacheKey(), amg);
		}

	}

	@Override
	public void warmUpAdTemplate() {
		String warmUpTime = stringValueOperations.get(Constants.WARM_UP_TIME);
		List<AdTemplate> adTemplates = null;
		if (StringUtils.isEmpty(warmUpTime)) {
			adTemplates = adTemplateDao.findAll();
		} else {
			adTemplates = adTemplateDao.findAfterTime(warmUpTime);
		}
		if (CollectionUtils.isEmpty(adTemplates))
			return;
		for (AdTemplate at : adTemplates) {
			this.adTemplateOperations.set(at.toCacheKey(), at);
		}

	}

	public void warmUpCreativeAndMaterialGroupMap(long creativeId) {
		List<Integer> amgIds = adMaterialGroupDao.findIdsByCreativeId(creativeId);
		if (!CollectionUtils.isEmpty(amgIds)) {
			mappingValueOperations.set(Creative.materialGroupIdsCacheKey(creativeId), amgIds);
		}
	}

	@Override
	public void warmUpAdTemplateSpec() {
		String warmUpTime = stringValueOperations.get(Constants.WARM_UP_TIME);
		List<AdTemplateSpec> atss = null;
		if (StringUtils.isEmpty(warmUpTime)) {
			atss = adTemplateSpecDao.findAll();
		} else {
			atss = adTemplateSpecDao.findAfterTime(warmUpTime);
		}
		if (CollectionUtils.isEmpty(atss))
			return;
		for (AdTemplateSpec ats : atss) {
			this.adTemplateSpecOperations.set(ats.toCacheKey(), ats);
		}
	}

	AdTemplateSpec findAdTemplateSpecById(long id) {
		return adTemplateSpecOperations.get(AdTemplateSpec.cacheKey(id));
	}


	@Override
	public long countUserAndCreativeClick(long cid, String uniqueId) {
		if (cid <= 0 || StringUtils.isEmpty(uniqueId))
			return 0;
		String key = this.toUserAndCreativeClickKey(cid, uniqueId);
		String timeKey = key + ":time";
		long i = countOperations.increment(key, 1);
		countOperations.set(timeKey, System.currentTimeMillis());
		if (i == 1) {
			redisTemplate.expire(key, 25, TimeUnit.HOURS);
			redisTemplate.expire(timeKey, 25, TimeUnit.HOURS);

		}
		return i;
	}

	@Override
	public long countUserAndCreativeImpressions(long cid, String uniqueId) {
		if (cid <= 0 || StringUtils.isEmpty(uniqueId))
			return 0;
		String key = toUserAndCreativeImpressionsKey(cid, uniqueId);
		String timeKey = key + ":time";
		long i = countOperations.increment(key, 1);
		countOperations.set(timeKey, System.currentTimeMillis());
		if (i == 1) {
			this.redisTemplate.expire(key, 25, TimeUnit.HOURS);
			redisTemplate.expire(timeKey, 25, TimeUnit.HOURS);
		}
		return i;
	}

	String toUserAndCreativeClickKey(long cid, String uniqueId) {
		String today = DateTime.now().toString("yyyy-MM-dd");
		StringBuilder keySB = new StringBuilder(today).append(":").append(uniqueId).append(":").append(cid).append(":")
				.append("click");
		return keySB.toString();
	}

	String toUserAndCreativeImpressionsKey(long cid, String uniqueId) {
		String today = DateTime.now().toString("yyyy-MM-dd");
		StringBuilder keySB = new StringBuilder(today).append(":").append(uniqueId).append(":").append(cid).append(":")
				.append("impressions");
		return keySB.toString();
	}

	@Override
	public long fetchUserAndCreativeClick(long cid, String uniqueId) {
		return this.countOperations.get(toUserAndCreativeClickKey(cid, uniqueId));
	}

	@Override
	public long fetchUserAndCreativeImpressions(long cid, String uniqueId) {
		return this.countOperations.get(toUserAndCreativeImpressionsKey(cid, uniqueId));
	}

}
